
/**
 * AppleIIGo
 * GCR Disk Image class
 * (C) 2008 by Marc S. Ressl (mressl@umich.edu)
 * Released under the GPL
 */

#include <string.h>
#include "GCRDiskImage.h"
#include "FDIDiskImage.h"

UINT8 gcr53WriteMap[] = {
	0xab, 0xad, 0xae, 0xaf, 0xb5, 0xb6, 0xb7, 0xba,
	0xbb, 0xbd, 0xbe, 0xbf, 0xd6, 0xd7, 0xda, 0xdb,
	0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xed, 0xee, 0xef,
	0xf5, 0xf6, 0xf7, 0xfa, 0xfb, 0xfd, 0xfe, 0xff,
};

UINT8 gcr62WriteMap[] = {
	0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6,
	0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3,
	0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc,
	0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3,
	0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde,
	0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec,
	0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
	0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
};

GCRDiskImage::GCRDiskImage(UINT32 diskType,
						   char * diskImagePath,
						   UINT32 gcrType) :
FloppyDiskImage(diskType, diskImagePath) {
	gcrWriteMap = (gcrType == GCRTYPE_5_3) ? gcr53WriteMap : gcr62WriteMap;
	UINT32 gcrWriteMapByteNum = (gcrType == GCRTYPE_5_3) ?
		sizeof(gcr53WriteMap) : sizeof(gcr62WriteMap);
	
	memset(gcrReadMap, 0xff, sizeof(gcrReadMap));
	for (UINT32 index = 0; index < gcrWriteMapByteNum; index++)
		gcrReadMap[gcrWriteMap[index]] = index;
}

void GCRDiskImage::initGcrChecksum() {
	gcrChecksum = 0;
	isGcrErrorValue = false;
}

void GCRDiskImage::writeSync(UINT32 syncNum,
							   UINT32 syncBitNum,
							   UINT32 lastSyncBitNum) {
	while(syncNum--)
		writeNibble(0xff, syncNum ? syncBitNum : lastSyncBitNum);
}

void GCRDiskImage::writeFMValue(UINT8 value) {
	writeNibble((value >> 1) | 0xaa);
	writeNibble(value | 0xaa);
}

void GCRDiskImage::writeGcrValue(UINT8 value) {
	writeNibble(gcrWriteMap[value ^ gcrChecksum]);
	gcrChecksum = value;
}

void GCRDiskImage::writeGcrChecksum() {
	writeGcrValue(0);
}

bool GCRDiskImage::readDiskImageTrack(UINT32 diskImageTrackIndex) {
	UINT32 trackIndex, headIndex;
	UINT32 gcrTrackByteNum;
	getDiskTrack(&trackIndex, &headIndex, &gcrTrackByteNum,
				 diskImageTrackIndex);
	
	UINT8 gcrTrackData[gcrTrackByteNum];
	if (fread(gcrTrackData, gcrTrackByteNum, 1, diskImageFile) != 1)
		return false;
	
	setTrackIndex(trackIndex);
	setHeadIndex(headIndex);
	writeTrack(diskImageTrackIndex, gcrTrackData);
	
	return true;
}

bool GCRDiskImage::readDiskImage(UINT32 diskImageOffset,
								 UINT32 diskImageTrackNum) {
	fseek(diskImageFile, diskImageOffset, SEEK_SET);
	
	for (UINT32 index = 0; index < diskImageTrackNum; index++)
		if (!readDiskImageTrack(index))
			return false;
	
	return true;
}

UINT8 GCRDiskImage::readFMValue() {
	UINT8 value = (readNibble() << 1) | 0x01;
	return readNibble() & value;
}

UINT8 GCRDiskImage::readGcrValue() {
	UINT8 value = gcrReadMap[readNibble()];
	if (value == 0xff) {
		value = 0;
		isGcrErrorValue = true;
	}
	
	value ^= gcrChecksum;
	gcrChecksum = value;
	
	return value;
}

bool GCRDiskImage::isGcrError(void) {
	return isGcrErrorValue;
}

bool GCRDiskImage::checkGcrChecksum() {
	return !isGcrErrorValue && (readGcrValue() == 0);
}

bool GCRDiskImage::writeDiskImageTrack(UINT32 trackIndex, UINT32 headIndex,
									   bool isWrite) {
	UINT32 diskImageTrackIndex, diskImageTrackByteNum;
	getDiskImageTrack(&diskImageTrackIndex, &diskImageTrackByteNum,
					  trackIndex, headIndex);
	
	setTrackIndex(trackIndex);
	setHeadIndex(headIndex);
	
	if (!diskImageTrackByteNum) {
		if (getNibbleNum() != 0)
			return false;
	} else {
		UINT8 diskImageTrackData[diskImageTrackByteNum];
		if (!readTrack(diskImageTrackIndex, diskImageTrackData))
			return false;
		if (isWrite && fwrite(diskImageTrackData,
							  diskImageTrackByteNum,
							  1,
							  diskImageFile) != 1)
			return false;
	}
	
	return true;
}

bool GCRDiskImage::writeDiskImage(UINT32 diskImageOffset, bool isWrite) {
	fseek(diskImageFile, diskImageOffset, SEEK_SET);
	
	for (UINT32 trackIndex = 0; trackIndex < getTrackNum(); trackIndex++)
		for (UINT32 headIndex = 0; headIndex < getHeadNum(); headIndex++)
			if (!writeDiskImageTrack(trackIndex, headIndex, isWrite))
				return false;
	
	return true;
}
